#pragma once

#include <stx/core/concepts.hpp>

inline namespace stx
{
template<typename T>
concept IsAnsiChar = IsIdentical<T, char>;

template<typename T>
concept IsStdChar = EXPR(IsIdentical<T, char> || IsIdentical<T, wchar_t>);

template<typename T>
concept IsRawStdChar = IsStdChar<T> && IsRaw<T>;

template<typename T>
concept IsExStdChar = EXPR(IsIdentical<T, char> || IsIdentical<T, wchar_t> ||
                           IsIdentical<T, char8_t> ||
                           IsIdentical<T, char16_t> ||
                           IsIdentical<T, char32_t>);

template<typename T>
concept IsRawExStdChar = IsExStdChar<T> && IsRaw<T>;

template<typename T>
concept IsConstructibleFromExStdStringView =
    std::constructible_from<std::basic_string_view<char>, T const&> ||
    std::constructible_from<std::basic_string_view<wchar_t>, T const&> ||
    std::constructible_from<std::basic_string_view<char8_t>, T const&> ||
    std::constructible_from<std::basic_string_view<char16_t>, T const&> ||
    std::constructible_from<std::basic_string_view<char32_t>, T const&>;

template<typename T>
concept IsStringLike1__ = requires(T const& obj)
{
    requires !IsIdentical<T, std::nullptr_t>;
    requires IsConstructibleFromExStdStringView<T>;
};

template<typename T>
concept IsStringLike2__ = IsContiguousRange<NoCvr<T>> && requires(T const& obj)
{
    requires IsExStdChar<decltype(*std::begin(obj))>;
    requires IsExStdChar<decltype(*std::data(obj))>;
    requires IsIdentical<decltype(*std::begin(obj)), decltype(*std::data(obj))>;
};

template<typename T>
concept IsStringLike = IsStringLike1__<T> || IsStringLike2__<T>;

template<IsStringLike T>
requires IsConstructibleFromExStdStringView<T>
auto getStringCharType__(T const& obj) -> NoCvr<decltype(obj[0])>;

template<IsStringLike T>
requires EXPR(!IsConstructibleFromExStdStringView<T>)
auto getStringCharType__(T const& obj) -> NoCvr<decltype(*std::begin(obj))>;

template<IsStringLike T>
using StringCharType =
    NoCvr<decltype(getStringCharType__(std::declval<T const&>()))>;

template<typename T>
concept IsAnsiCharStringLike = IsStringLike<T> &&
    EXPR(IsIdentical<StringCharType<T>, char>);

template<typename T>
concept IsStdCharStringLike = IsStringLike<T> && requires
{
    requires IsStdChar<StringCharType<T>>;
};

template<typename T>
concept IsExStdClassicString1__ = IsPointer<T> && requires(T& sz)
{
    requires IsExStdChar<decltype(sz[0])>;
};

template<typename T>
concept IsExStdClassicString2__ = IsExStdClassicString1__<std::decay_t<T>>;

template<typename T>
concept IsExStdClassicString =
    IsExStdClassicString1__<T> || IsExStdClassicString2__<T>;

template<typename T>
concept IsStdClassicString = IsExStdClassicString<T> && requires(T& sz)
{
    requires IsStdChar<decltype(sz[0])>;
};

template<typename Head_, typename... Tail_>
concept AreSimilarStrings =
    AllTrue<(IsStringLike<Head_> && ... && IsStringLike<Tail_>),
            (IsIdentical<StringCharType<Head_>, StringCharType<Tail_>> && ...)>;

template<typename T>
concept IsStringLikeRange = IsRange<T> && requires
{
    requires IsStringLike<RangeValueType<T>>;
};

// std non-container
// -----------------
// - std::tuple
// - std::pair
// - std::optional

// std containers
// --------------
// - std::initializer_list
// - std::array
// - std::span
// - std::stack
// - std::queue
// - std::priority_queue
// - std::string
// - std::string_view
// - std::vector
// - std::deque
// - std::list
// - std::forward_list
// - std::set
// - std::map
// - std::multiset
// - std::multimap
// - std::unordered_set
// - std::unordered_map
// - std::unordered_multiset
// - std::unordered_multimap

template<IsAnyType>
struct IsStdOptional__ final : std::false_type
{};

template<IsNonVoid T>
struct IsStdOptional__<std::optional<T>> final : std::true_type
{};

template<typename T>
concept IsStdOptional = IsStdOptional__<NoCvr<T>>::value;

template<IsAnyType>
struct IsStdTuple__ final : std::false_type
{};

template<IsNonVoid... Args_>
struct IsStdTuple__<std::tuple<Args_...>> final : std::true_type
{};

template<IsNonVoid T, IsNonVoid U>
struct IsStdTuple__<std::pair<T, U>> final : std::true_type
{};

template<typename T>
concept IsStdTuple = IsStdTuple__<NoCvr<T>>::value;

template<IsAnyType>
struct IsStdInitializerList__ final : std::false_type
{};

template<template<typename> typename T, IsNonVoid A>
struct IsStdInitializerList__<T<A>> final
    : std::bool_constant<IsBaseOf<std::initializer_list<A>, T<A>>>
{};

template<typename T>
concept IsStdInitializerList = IsStdInitializerList__<NoCvr<T>>::value;

template<IsAnyType>
struct IsStdArray__ final : std::false_type
{};

template<template<typename, std::size_t> typename T, IsNonVoid A, std::size_t N>
struct IsStdArray__<T<A, N>> final
    : std::bool_constant<IsBaseOf<std::array<A, N>, T<A, N>>>
{};

template<typename T>
concept IsStdArray = IsStdArray__<NoCvr<T>>::value;

template<IsAnyType>
struct IsStdSpan__ final : std::false_type
{};

template<template<typename, std::size_t> typename T, IsNonVoid A, std::size_t N>
struct IsStdSpan__<T<A, N>> final
    : std::bool_constant<IsBaseOf<std::span<A, N>, T<A, N>>>
{};

template<typename T>
concept IsStdSpan = IsStdSpan__<NoCvr<T>>::value;

template<IsAnyType>
struct IsStdStack__ final : std::false_type
{};

template<template<typename, typename> typename T, IsNonVoid A1, IsNonVoid A2>
struct IsStdStack__<T<A1, A2>> final
    : std::bool_constant<IsBaseOf<std::stack<A1, A2>, T<A1, A2>>>
{};

template<typename T>
concept IsStdStack = IsStdStack__<NoCvr<T>>::value;

template<IsAnyType>
struct IsStdQueue__ final : std::false_type
{};

template<template<typename, typename> typename T, IsNonVoid A1, IsNonVoid A2>
struct IsStdQueue__<T<A1, A2>> final
    : std::bool_constant<IsBaseOf<std::queue<A1, A2>, T<A1, A2>>>
{};

template<typename T>
concept IsStdQueue = IsStdQueue__<NoCvr<T>>::value;

template<IsAnyType>
struct IsStdPriorityQueue__ final : std::false_type
{};

template<template<typename, typename, typename> typename T,
         IsNonVoid A1,
         IsNonVoid A2,
         IsNonVoid A3>
struct IsStdPriorityQueue__<T<A1, A2, A3>> final
    : std::bool_constant<
          IsBaseOf<std::priority_queue<A1, A2, A3>, T<A1, A2, A3>>>
{};

template<typename T>
concept IsStdPriorityQueue = IsStdPriorityQueue__<NoCvr<T>>::value;

template<IsAnyType>
struct IsStdStringView__ final : std::false_type
{};

template<template<typename, typename> typename T, IsRawStdChar A1, IsNonVoid A2>
struct IsStdStringView__<T<A1, A2>> final
    : std::bool_constant<IsBaseOf<std::basic_string_view<A1, A2>, T<A1, A2>>>
{};

template<typename T>
concept IsStdStringView = IsStdStringView__<NoCvr<T>>::value;

template<IsAnyType>
struct IsExStdStringView__ final : std::false_type
{};

template<template<typename, typename> typename T,
         IsRawExStdChar A1,
         IsNonVoid      A2>
struct IsExStdStringView__<T<A1, A2>> final
    : std::bool_constant<IsBaseOf<std::basic_string_view<A1, A2>, T<A1, A2>>>
{};

template<typename T>
concept IsExStdStringView = IsExStdStringView__<NoCvr<T>>::value;

template<IsAnyType>
struct IsStdString__ final : std::false_type
{};

template<template<typename, typename, typename> typename T,
         IsRawStdChar A1,
         IsNonVoid    A2,
         IsNonVoid    A3>
struct IsStdString__<T<A1, A2, A3>> final
    : std::bool_constant<IsBaseOf<std::basic_string<A1, A2, A3>, T<A1, A2, A3>>>
{};

template<typename T>
concept IsStdString = IsStdString__<NoCvr<T>>::value;

template<IsAnyType>
struct IsExStdString__ final : std::false_type
{};

template<template<typename, typename, typename> typename T,
         IsExStdChar A1,
         IsNonVoid   A2,
         IsNonVoid   A3>
struct IsExStdString__<T<A1, A2, A3>> final
    : std::bool_constant<IsBaseOf<std::basic_string<A1, A2, A3>, T<A1, A2, A3>>>
{};

template<typename T>
concept IsExStdString = IsExStdString__<NoCvr<T>>::value;

template<IsAnyType>
struct IsStdVector__ final : std::false_type
{};

template<template<typename, typename> typename T, IsNonVoid A1, IsNonVoid A2>
struct IsStdVector__<T<A1, A2>> final
    : std::bool_constant<IsBaseOf<std::vector<A1, A2>, T<A1, A2>>>
{};

template<typename T>
concept IsStdVector = IsStdVector__<NoCvr<T>>::value;

template<IsAnyType>
struct IsStdDeque__ final : std::false_type
{};

template<template<typename, typename> typename T, IsNonVoid A1, IsNonVoid A2>
struct IsStdDeque__<T<A1, A2>> final
    : std::bool_constant<IsBaseOf<std::deque<A1, A2>, T<A1, A2>>>
{};

template<typename T>
concept IsStdDeque = IsStdDeque__<NoCvr<T>>::value;

template<IsAnyType>
struct IsStdForwardList__ final : std::false_type
{};

template<template<typename, typename> typename T, IsNonVoid A1, IsNonVoid A2>
struct IsStdForwardList__<T<A1, A2>> final
    : std::bool_constant<IsBaseOf<std::forward_list<A1, A2>, T<A1, A2>>>
{};

template<typename T>
concept IsStdForwardList = IsStdForwardList__<NoCvr<T>>::value;

template<IsAnyType>
struct IsStdList__ final : std::false_type
{};

template<template<typename, typename> typename T, IsNonVoid A1, IsNonVoid A2>
struct IsStdList__<T<A1, A2>> final
    : std::bool_constant<IsBaseOf<std::list<A1, A2>, T<A1, A2>>>
{};

template<typename T>
concept IsStdList = IsStdList__<NoCvr<T>>::value;

template<IsAnyType>
struct IsStdSet__ final : std::false_type
{};

template<template<typename, typename, typename> typename T,
         IsNonVoid A1,
         IsNonVoid A2,
         IsNonVoid A3>
struct IsStdSet__<T<A1, A2, A3>> final
    : std::bool_constant<IsBaseOf<std::set<A1, A2, A3>, T<A1, A2, A3>>>
{};

template<typename T>
concept IsStdSet = IsStdSet__<NoCvr<T>>::value;

template<IsAnyType>
struct IsStdMultiset__ final : std::false_type
{};

template<template<typename, typename, typename> typename T,
         IsNonVoid A1,
         IsNonVoid A2,
         IsNonVoid A3>
struct IsStdMultiset__<T<A1, A2, A3>> final
    : std::bool_constant<IsBaseOf<std::multiset<A1, A2, A3>, T<A1, A2, A3>>>
{};

template<typename T>
concept IsStdMultiset = IsStdMultiset__<NoCvr<T>>::value;

template<IsAnyType>
struct IsStdMap__ final : std::false_type
{};

template<template<typename, typename, typename, typename> typename T,
         IsNonVoid A1,
         IsNonVoid A2,
         IsNonVoid A3,
         IsNonVoid A4>
struct IsStdMap__<T<A1, A2, A3, A4>> final
    : std::bool_constant<IsBaseOf<std::map<A1, A2, A3, A4>, T<A1, A2, A3, A4>>>
{};

template<typename T>
concept IsStdMap = IsStdMap__<NoCvr<T>>::value;

template<IsAnyType>
struct IsStdMultimap__ final : std::false_type
{};

template<template<typename, typename, typename, typename> typename T,
         IsNonVoid A1,
         IsNonVoid A2,
         IsNonVoid A3,
         IsNonVoid A4>
struct IsStdMultimap__<T<A1, A2, A3, A4>> final
    : std::bool_constant<
          IsBaseOf<std::multimap<A1, A2, A3, A4>, T<A1, A2, A3, A4>>>
{};

template<typename T>
concept IsStdMultimap = IsStdMultimap__<NoCvr<T>>::value;

template<IsAnyType>
struct IsStdUnorderedSet__ final : std::false_type
{};

template<template<typename, typename, typename, typename> typename T,
         IsNonVoid A1,
         IsNonVoid A2,
         IsNonVoid A3,
         IsNonVoid A4>
struct IsStdUnorderedSet__<T<A1, A2, A3, A4>> final
    : std::bool_constant<
          IsBaseOf<std::unordered_set<A1, A2, A3, A4>, T<A1, A2, A3, A4>>>
{};

template<typename T>
concept IsStdUnorderedSet = IsStdUnorderedSet__<NoCvr<T>>::value;

template<IsAnyType>
struct IsStdUnorderedMultiset__ final : std::false_type
{};

template<template<typename, typename, typename, typename> typename T,
         IsNonVoid A1,
         IsNonVoid A2,
         IsNonVoid A3,
         IsNonVoid A4>
struct IsStdUnorderedMultiset__<T<A1, A2, A3, A4>> final
    : std::bool_constant<
          IsBaseOf<std::unordered_multiset<A1, A2, A3, A4>, T<A1, A2, A3, A4>>>
{};

template<typename T>
concept IsStdUnorderedMultiset = IsStdUnorderedMultiset__<NoCvr<T>>::value;

template<IsAnyType>
struct IsStdUnorderedMap__ final : std::false_type
{};

template<template<typename, typename, typename, typename, typename> typename T,
         IsNonVoid A1,
         IsNonVoid A2,
         IsNonVoid A3,
         IsNonVoid A4,
         IsNonVoid A5>
struct IsStdUnorderedMap__<T<A1, A2, A3, A4, A5>> final
    : std::bool_constant<IsBaseOf<std::unordered_map<A1, A2, A3, A4, A5>,
                                  T<A1, A2, A3, A4, A5>>>
{};

template<typename T>
concept IsStdUnorderedMap = IsStdUnorderedMap__<NoCvr<T>>::value;

template<IsAnyType>
struct IsStdUnorderedMultimap__ final : std::false_type
{};

template<template<typename, typename, typename, typename, typename> typename T,
         IsNonVoid A1,
         IsNonVoid A2,
         IsNonVoid A3,
         IsNonVoid A4,
         IsNonVoid A5>
struct IsStdUnorderedMultimap__<T<A1, A2, A3, A4, A5>> final
    : std::bool_constant<IsBaseOf<std::unordered_multimap<A1, A2, A3, A4, A5>,
                                  T<A1, A2, A3, A4, A5>>>
{};

template<typename T>
concept IsStdUnorderedMultimap = IsStdUnorderedMultimap__<NoCvr<T>>::value;

template<typename T>
concept IsStdInsertableContainer =
    IsStdStack<T> || IsStdQueue<T> || IsStdPriorityQueue<T> ||
    IsExStdString<T> || IsStdVector<T> || IsStdDeque<T> || IsStdList<T> ||
    IsStdForwardList<T> || IsStdSet<T> || IsStdMap<T> || IsStdMultiset<T> ||
    IsStdMultimap<T> || IsStdUnorderedSet<T> || IsStdUnorderedMap<T> ||
    IsStdUnorderedMultiset<T> || IsStdUnorderedMultimap<T>;

template<typename T>
concept IsStdAssociativeContainer =
    IsStdSet<T> || IsStdMap<T> || IsStdMultiset<T> || IsStdMultimap<T> ||
    IsStdUnorderedSet<T> || IsStdUnorderedMap<T> || IsStdUnorderedMultiset<T> ||
    IsStdUnorderedMultimap<T>;

template<typename T>
concept IsStdMultiAssociativeContainer = IsStdMultiset<T> || IsStdMultimap<T> ||
    IsStdUnorderedMultiset<T> || IsStdUnorderedMultimap<T>;

template<typename T>
concept IsOfStdSetFamily = IsStdSet<T> || IsStdMultiset<T> ||
    IsStdUnorderedSet<T> || IsStdUnorderedMultiset<T>;

template<typename T>
concept IsOfStdMapFamily = IsStdAssociativeContainer<T> && !IsOfStdSetFamily<T>;

template<typename T>
concept IsStdInsertableSequenceContainer =
    IsStdInsertableContainer<T> && !IsStdAssociativeContainer<T>;

using SystemClock = std::chrono::system_clock;
using FileClock   = std::chrono::file_clock;
using SteadyClock = std::chrono::steady_clock;

template<IsNonVoid Clock_, IsNonVoid Duration_ = typename Clock_::duration>
using TimePoint = std::chrono::time_point<Clock_, Duration_>;

using SysTimePoint  = TimePoint<SystemClock>;
using FileTimePoint = TimePoint<FileClock>;
using RelTimePoint  = TimePoint<SteadyClock>;
using Weeks         = std::chrono::weeks;
using Days          = std::chrono::days;
using Hours         = std::chrono::hours;
using Minutes       = std::chrono::minutes;
using Seconds       = std::chrono::seconds;
using Milliseconds  = std::chrono::milliseconds;
using Microseconds  = std::chrono::microseconds;
using Nanoseconds   = std::chrono::nanoseconds;

template<IsAnyType>
struct IsAbsTimePoint__ final : std::false_type
{};

template<IsNonVoid T>
struct IsAbsTimePoint__<TimePoint<SystemClock, T>> final : std::true_type
{};

template<IsNonVoid T>
struct IsAbsTimePoint__<TimePoint<FileClock, T>> final : std::true_type
{};

template<typename T>
concept IsAbsTimePoint = IsAbsTimePoint__<NoCvr<T>>::value;

template<IsAnyType>
struct IsRelTimePoint__ final : std::false_type
{};

template<IsNonVoid T>
struct IsRelTimePoint__<TimePoint<SteadyClock, T>> final : std::true_type
{};

template<typename T>
concept IsRelTimePoint = IsRelTimePoint__<NoCvr<T>>::value;

template<typename T>
concept IsStdTimePoint = IsAbsTimePoint<T> || IsRelTimePoint<T>;

template<IsAnyType>
struct IsStdDuration__ final : std::false_type
{};

template<IsNonVoid Rep_, IsNonVoid Period_>
struct IsStdDuration__<std::chrono::duration<Rep_, Period_>> final
    : std::true_type
{};

template<typename T>
concept IsStdDuration = IsStdDuration__<NoCvr<T>>::value;

template<typename T>
concept IsRawStdDuration = IsStdDuration<T> && IsRaw<T>;

template<typename T>
concept IsStxDuration = requires
{
    static_cast<Type<typename T::StxDurationTag_>*>(nullptr);
};

template<typename T>
concept IsRawStxDuration = IsStxDuration<T> && IsRaw<T>;

template<typename T>
concept IsDurationLike = IsStdDuration<T> || IsStxDuration<T>;

template<typename T>
concept IsStdRegex = IsIdentical<T, std::regex> || IsIdentical<T, std::wregex>;
} // namespace stx